package org.unidata.mdm.rest.v1.data.service.search;

import java.util.Map;
import java.util.Objects;

import org.unidata.mdm.meta.type.search.EntityIndexType;
import org.unidata.mdm.meta.type.search.EtalonIndexType;
import org.unidata.mdm.rest.v1.data.module.DataRestModule;
import org.unidata.mdm.rest.v1.search.converter.SearchResultToRestSearchResultConverter;
import org.unidata.mdm.rest.v1.search.ro.SearchResponseRO;
import org.unidata.mdm.rest.v1.search.ro.SearchResultRO;
import org.unidata.mdm.search.context.ComplexSearchRequestContext;
import org.unidata.mdm.search.dto.ComplexSearchResultDTO;
import org.unidata.mdm.search.dto.SearchOutputContainer;
import org.unidata.mdm.search.dto.SearchResultDTO;
import org.unidata.mdm.system.dto.OutputContainer;
import org.unidata.mdm.system.type.rendering.OutputFragmentRenderer;
import org.unidata.mdm.system.type.rendering.OutputSink;
import org.unidata.mdm.system.util.JsonUtils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;

/**
 * Search response post processor
 * Fix response hierarchy
 *
 * @author Alexandr Serov
 * @since 05.12.2020
 **/
public class HierarchicalSearchOutputFinalizer implements OutputFragmentRenderer {

    private static final TypeReference<Map<String, Object>> MAP_TYPE_REF = new TypeReference<>() {
    };

    private static final String PAYLOAD_PROPERTY = "payload";

    private static final int LATEST_ORDER = 10_000;

    private final SearchResultHitModifier searchResultHitModifier;

    public HierarchicalSearchOutputFinalizer(SearchResultHitModifier searchResultHitModifier) {
        this.searchResultHitModifier = searchResultHitModifier;
    }

    @Override
    public void render(String version, OutputContainer container, OutputSink sink) {
        Objects.requireNonNull(container, "Container can't be null");

        if (container instanceof SearchResultDTO) {
            if (!((SearchResultDTO) container).getIndexType()
                    .isOneOf(EtalonIndexType.ETALON, EntityIndexType.RECORD, EntityIndexType.RELATION)) {
                return;
            }
        }

        if (container instanceof ComplexSearchResultDTO) {
            if (!((ComplexSearchResultDTO) container).getMain().getIndexType()
                    .isOneOf(EtalonIndexType.ETALON, EntityIndexType.RECORD, EntityIndexType.RELATION)) {
                return;
            }
        }

        if (sink instanceof SearchResponseRO) {
//            ObjectMapper objectMapper = Objects.requireNonNull(JsonUtils.getMapper(), "ObjectMapper not preset in JsonUtils");
            SearchOutputContainer output = (SearchOutputContainer) container;
            SearchResponseRO response = (SearchResponseRO) sink;
            SearchResultRO searchResult = mapOrCreatePayloadSection(response);
//            SearchResultRO searchResult = Optional.ofNullable(payload.get(DataRestModule.MODULE_ID))
//                .map(exists -> objectMapper.convertValue(exists, SearchResultRO.class))
//                .orElseGet(SearchResultRO::new);
//            payload.put(DataRestModule.MODULE_ID, searchResult);
            if (SearchOutputContainer.SearchOutputContainerType.COMPLEX == output.getContainerType()) {
                ComplexSearchResultDTO collected = (ComplexSearchResultDTO) output;
                if (ComplexSearchRequestContext.ComplexSearchRequestType.HIERARCHICAL == collected.getComplexSearchType()) {
                    SearchResultDTO main = collected.getMain();
                    searchResultHitModifier.modifySearchResult(main);
                    SearchResultRO local = SearchResultToRestSearchResultConverter.convert(main);
                    searchResult.setFields(local.getFields());
                    searchResult.setHasRecords(local.isHasRecords());
                    searchResult.setMaxScore(local.getMaxScore());
                    searchResult.setTotalCount(local.getTotalCount());
                    searchResult.setTotalCountLimit(local.getTotalCountLimit());
                    searchResult.getHits().addAll(local.getHits());
                }
            }
            response.setAny(DataRestModule.MODULE_ID, JsonUtils.writeNode(searchResult));
        }
    }

    private SearchResultRO mapOrCreatePayloadSection(SearchResponseRO response) {
        Map<String, JsonNode> nodes = response.getAny();
        JsonNode payloadNode = nodes.get(DataRestModule.MODULE_ID);
        // Map<String, Object> result = new HashMap<>();
        // ObjectMapper objectMapper = Objects.requireNonNull(JsonUtils.getMapper(), "ObjectMapper not preset in JsonUtils");
        if (payloadNode != null && !payloadNode.isNull()) {
            return JsonUtils.read(payloadNode, SearchResultRO.class);
        }

        return new SearchResultRO();
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public int order() {
        return LATEST_ORDER;
    }
}
